﻿//-----------------------------------------------------------------------------------------------------------
// WBFSSync Project by Omega Frost 
// http://wbfssync.codeplex.com/
//
// WBFSSync is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using WBFSe3.IO;

namespace WbfsSync.Core
{
    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    public static class Extensions
    {
        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        enum Compression
        {
            BI_RGB = 0,
            BI_RLE8 = 1,
            BI_RLE4 = 2,
            BI_BITFIELDS = 3,
            BI_JPEG = 4,
            BI_PNG = 5,
        }

        struct IconGroupHeader
        {
            public ushort Reserved;
            public ushort Type;
            public ushort Count;

            public IconDirEntry[] Entries;
        }

        struct IconDirEntry
        {
            public byte Width;
            public byte Height;
            public byte ColorCount;
            public byte Reserved;
            public ushort Planes;
            public ushort BitCount;
            public int NumBytes;
            public ushort Id;
        }

        class PtrReference
        {
            public IntPtr p;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static readonly IWshRuntimeLibrary.WshShell shell =
            new IWshRuntimeLibrary.WshShell();

        //public static String IconCacheFolder = "cache\\";

        private static ImageSource[] ShortcutFrames = null;
        private static ImageSource[] FolderFrames = null;
        private static ImageSource[] FileFrames = null;


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static bool IsLnk(FileInfo fi)
        {
            return String.Compare(fi.Extension, ".lnk", true) == 0;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static bool IsURL(FileInfo fi)
        {
            return String.Compare(fi.Extension, ".url", true) == 0;
        }

        
        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static String GetExtensionName(String extension)
        {
            if (String.IsNullOrEmpty(extension))
                return "File";

            try
            {
                RegistryKey k = Registry.ClassesRoot.OpenSubKey(extension);
                if (k == null) goto DEFAULT;

                String type = k.GetValue(null) as String;
                if (String.IsNullOrEmpty(type)) goto DEFAULT;

                k = Registry.ClassesRoot.OpenSubKey(type);
                if (k == null) goto DEFAULT;

                return k.GetValue(null) as String;

            DEFAULT:
                return extension.Substring(1).ToUpper() + " File";
            }
            catch
            {
                return String.Empty;
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static bool GetShortcutPath(FileInfo fi, out string path, out String icon)
        {
            try
            {
                IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)
                            shell.CreateShortcut(fi.FullName);

                path = shortcut.TargetPath;
                icon = shortcut.IconLocation;

                if (String.IsNullOrEmpty(icon) || (icon.StartsWith(",")))
                    icon = path;

                return true;
            }
            catch
            {
                path = fi.FullName;
                icon = fi.FullName;

                return false;
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static bool GetURLPath(FileInfo fi, out string path, out String icon)
        {
            path = String.Empty;
            icon = String.Empty;

            foreach (String line in File.ReadAllLines(fi.FullName))
            {
                if (line.StartsWith("URL=", StringComparison.OrdinalIgnoreCase))
                    path = line.Substring(4);
                else if (line.StartsWith("IconFile=", StringComparison.OrdinalIgnoreCase))
                    icon = icon.Insert(0, line.Substring(9));
                else if (line.StartsWith("IconIndex=", StringComparison.OrdinalIgnoreCase))
                    icon = icon.Insert(icon.Length, "," + line.Substring(10));
            }

            if (String.IsNullOrEmpty(icon))
                icon = path;

            return true;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource GetFolderIcon(DirectoryInfo di, int size)
        {
            ImageSource[] frames = null;
            String inifile = Path.Combine(di.FullName, "desktop.ini");

            if (!Config.DisableFolderIconLoading && File.Exists(inifile))
            {
                try
                {
                    String iconFile = null;
                    int iconIndex = 0;

                    String[] lines = File.ReadAllLines(inifile);
                    foreach (String line in lines)
                    {
                        if (line.StartsWith("IconFile="))
                            iconFile = line.Substring(9);
                        else if (line.StartsWith("IconIndex="))
                            iconIndex = int.Parse(line.Substring(10));
                    }

                    if (!Path.IsPathRooted(iconFile))
                        iconFile = Path.Combine(di.FullName, iconFile);

                    if (iconFile.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(iconFile);
                    }
                    else if (iconFile.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ||
                        iconFile.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(GetIconFromModule(iconFile, iconIndex));
                    }
                }
                catch
                {
                    frames = null;
                }
            }

            if (frames == null)
            {
                if (FolderFrames == null)
                    FolderFrames = LoadFrames(@"pack://application:,,/Icons/Browser/icon_folder/");

                frames = FolderFrames;
            }

            return SelectIcon(frames, size);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource GetFileIcon(FileInfo fi, int size)
        {
            ImageSource[] frames = null;
            if (!Config.DisableFileIconLoading && !String.IsNullOrEmpty(fi.Extension))
            {
                try
                {
                    String iconFile = null;
                    int iconIndex = 0;

                    if (fi.Extension.ToLower() == ".exe")
                        iconFile = fi.FullName;
                    else
                    {
                        RegistryKey k = Registry.ClassesRoot.OpenSubKey(fi.Extension);
                        if (k == null) goto END;

                        String type = k.GetValue(null) as String;
                        if (String.IsNullOrEmpty(type)) goto END;

                        k = Registry.ClassesRoot.OpenSubKey(type);
                        if (k == null) goto END;

                        k = k.OpenSubKey("DefaultIcon");
                        if (k == null) goto END;

                        String value = k.GetValue(null) as String;
                        int sep;

                        if ((sep = value.IndexOf(',')) < 0)
                            iconFile = value;
                        else
                        {
                            iconFile = value.Substring(0, sep);
                            iconIndex = int.Parse(value.Substring(sep + 1));

                            if (iconFile[0] == '"')
                                iconFile = iconFile.Remove(0, 1);

                            if (iconFile[iconFile.Length - 1] == '"')
                                iconFile = iconFile.Remove(iconFile.Length - 1);

                            if (iconFile[iconFile.Length - 1] == '\\')
                                iconFile = iconFile.Remove(iconFile.Length - 1);
                        }
                    }

                    if (iconFile.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(iconFile);
                    }
                    else if (iconFile.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ||
                        iconFile.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(GetIconFromModule(iconFile, iconIndex));
                    }
                }
                catch
                {
                    frames = null;
                }
            }

        END:

            if (frames == null)
            {
                if (FileFrames == null)
                    FileFrames = LoadFrames(@"pack://application:,,/Icons/Browser/icon_file/");

                frames = FileFrames;
            }

            return SelectIcon(frames, size);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource GetDriveIcon(WbfsDriveInfo drive, int size)
        {
            String icon = "pack://application:,,/Icons/Browser/";
            ImageSource[] frames = null;

            if (drive.IsWbfs || drive.IsFatWbfs)
            {
                icon += "icon_drive/";
            }
            else if (drive.IsCd)
            {
                if (drive.IsReady) icon += "icon_dvd/";
                else icon += "icon_dvddrive/";
            }
            else if (drive.IsFixed)
            {
                if (Char.ToUpper(Environment.SystemDirectory[0]) == drive.Name[0])
                    icon += "icon_wdrive/";
                else
                    icon += "icon_drive/";
            }
            else
                icon += "icon_rdrive/";

            frames = LoadFrames(icon);
            return SelectIcon(frames, size);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource GetFolderMiniature(String path)
        {
            String root = "pack://application:,,/Icons/Links/";
            String icon = null;

            ImageSourceConverter converter = 
                new ImageSourceConverter();

            path = path.ToLower();
            if (path == "%computer%\\") icon = "icon_comp.png";
            else if (path == "%desktop%\\") icon = "icon_desktop.png";
            else if (path == "%favorites%\\") icon = "icon_fav.png";
            else if (path == "%libraries%\\") icon = "icon_libs.png";
            else if (path == "%recent%\\") icon = "icon_recent.png";
            else if (path.EndsWith(":\\"))
            {
                WbfsDriveInfo drive;
                WbfsDriveInfo.GetDrive(path.
                    Substring(11), out drive);

                if (drive.IsWbfs || drive.IsFatWbfs)
                    icon = "icon_drive.png";
                else if (drive.IsCd)
                    icon = "icon_cd.png";
                else if (drive.IsFixed)
                {
                    if (Char.ToUpper(Environment.SystemDirectory[0]) == drive.Name[0])
                        icon = "icon_win.png";
                    else
                        icon = "icon_drive.png";
                }
                else
                    icon = "icon_removable.png";
            }

            if(icon == null) icon = "icon_folder.png";
            return converter.ConvertFromString(root + icon) as ImageSource;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource GetShortcutOverlay(int size)
        {
            if (ShortcutFrames == null)
                ShortcutFrames = LoadFrames(@"pack://application:,,/Icons/Browser/icon_shortcut/");

            return SelectIcon(ShortcutFrames, size);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource ParseIcon(String composite, int size)
        {
            ImageSource[] frames = null;

            if (!Config.DisableFileIconLoading)
            {
                try
                {
                    String iconFile = null;
                    int iconIndex = 0;
                    int sep;

                    if ((sep = composite.IndexOf(',')) < 0)
                        iconFile = composite;
                    else
                    {
                        iconFile = composite.Substring(0, sep);
                        iconIndex = int.Parse(composite.Substring(sep + 1));

                        if (iconFile[0] == '"')
                            iconFile = iconFile.Remove(0, 1);

                        if (iconFile[iconFile.Length - 1] == '"')
                            iconFile = iconFile.Remove(iconFile.Length - 1);

                        if (iconFile[iconFile.Length - 1] == '\\')
                            iconFile = iconFile.Remove(iconFile.Length - 1);
                    }

                    if (iconFile.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(iconFile);
                    }
                    else if (iconFile.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ||
                        iconFile.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                    {
                        frames = LoadFrames(GetIconFromModule(iconFile, iconIndex));
                    }
                }
                catch
                {
                    frames = null;
                }
            }

            if (frames == null)
            {
                if (FileFrames == null)
                    FileFrames = LoadFrames(@"pack://application:,,/Icons/Browser/icon_file/");

                frames = FileFrames;
            }

            return SelectIcon(frames, size);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource LoadImageLocal(String source)
        {
            ImageSourceConverter converter = new ImageSourceConverter();
            return converter.ConvertFromString("pack://application:,,/" + source) as ImageSource;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static int SizeOrderer(BitmapFrame frame)
        {
            return (int)frame.Width;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        static ImageSource[] LoadFrames(String source)
        {
            if (source.EndsWith("/"))
            {
                ImageSourceConverter converter = 
                    new ImageSourceConverter();

                ImageSource[] frames = new ImageSource[6];
                frames[0] = (ImageSource)converter.ConvertFromString(source + "x16.png");
                frames[1] = (ImageSource)converter.ConvertFromString(source + "x24.png");
                frames[2] = (ImageSource)converter.ConvertFromString(source + "x32.png");
                frames[3] = (ImageSource)converter.ConvertFromString(source + "x48.png");
                frames[4] = (ImageSource)converter.ConvertFromString(source + "x96.png");
                frames[5] = (ImageSource)converter.ConvertFromString(source + "x256.png");

                return frames;
            }
            else
            {

                IconBitmapDecoder decoder = new IconBitmapDecoder(
                                new Uri(source,
                                UriKind.RelativeOrAbsolute),
                                BitmapCreateOptions.PreservePixelFormat,
                                BitmapCacheOption.Default);

                return decoder.Frames.OrderBy(SizeOrderer).ToArray();
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        static BitmapFrame[] LoadFrames(Stream source)
        {
            IconBitmapDecoder decoder = new IconBitmapDecoder(source,
                        BitmapCreateOptions.PreservePixelFormat,
                        BitmapCacheOption.Default);

            return decoder.Frames.OrderBy(SizeOrderer).ToArray();
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        static ImageSource SelectIcon(ImageSource[] frames, int size)
        {
            for (int i = 0; i < frames.Length; i++)
            {
                if (frames[i].Width >= size)
                    return frames[i];
            }

            return frames[frames.Length - 1];
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static Stream GetIconFromModule(String sModule, int iconIndex)
        {
            IntPtr module = IntPtr.Zero;
            IntPtr resource = IntPtr.Zero;
            IntPtr pMem = IntPtr.Zero;
            uint uSz = 0;

            MemoryStream stream = null;

            try
            {
                module = Native.LoadLibraryEx(sModule, IntPtr.Zero,
                    Native.LOAD_LIBRARY_AS_DATAFILE);

                if (module == IntPtr.Zero)
                    return null;

                PtrReference Address = new PtrReference();
                if (iconIndex >= 0)
                {
                    Address.p = new IntPtr(iconIndex);
                    Native.EnumResourceNames(module, new IntPtr(Native.RT_GROUP_ICON),
                        new Native.EnumResNameDelegate(EnumerateIcons), Address);

                    if (Address.p == IntPtr.Zero)
                        goto END;
                }
                else
                {
                    Address.p = new IntPtr(-iconIndex);
                }

                resource = Native.FindResourceEx(module,
                    new IntPtr(Native.RT_GROUP_ICON),
                    Address.p, Native.LANGID);

                if (resource == IntPtr.Zero)
                    goto END;

                uSz = Native.SizeofResource(module, resource);
                pMem = Native.LoadResource(module, resource);
                pMem = Native.LockResource(pMem);

                if (pMem == IntPtr.Zero)
                    goto END;

                Byte[] bGroupHeader = new Byte[uSz];
                Marshal.Copy(pMem, bGroupHeader, 0, (int)uSz);

                IconGroupHeader iconHeader =
                    ParsePEIconHeader(bGroupHeader);

                stream = new MemoryStream();
                MemoryStream data = new MemoryStream();

                // Get the maximum bitcount only

                IEnumerable<IconDirEntry> entries = null;

                entries = from e in iconHeader.Entries
                          where e.BitCount == 32
                          select e;

                if (entries.Count() == 0)
                {
                    entries = from e in iconHeader.Entries
                              where e.BitCount == 24
                              select e;

                    if (entries.Count() == 0)
                    {
                        entries = from e in iconHeader.Entries
                                  where e.BitCount == 16
                                  select e;

                        if (entries.Count() == 0)
                        {
                            entries = from e in iconHeader.Entries
                                      where e.BitCount == 8
                                      select e;

                            if (entries.Count() == 0)
                                entries = iconHeader.Entries;
                        }
                    }
                }

                // Header
                stream.Write(BitConverter.GetBytes((short)0), 0, 2);
                stream.Write(BitConverter.GetBytes((short)1), 0, 2);
                stream.Write(BitConverter.GetBytes((short)entries.Count()), 0, 2);

                foreach (IconDirEntry entry in entries)
                {
                    resource = Native.FindResourceEx(module,
                        new IntPtr(Native.RT_ICON),
                        new IntPtr(entry.Id), Native.LANGID);

                    pMem = Native.LoadResource(module, resource);
                    pMem = Native.LockResource(pMem);

                    Byte[] bHeader = new Byte[entry.NumBytes];
                    Marshal.Copy(pMem, bHeader, 0, entry.NumBytes);

                    // Header
                    stream.WriteByte(entry.Width);
                    stream.WriteByte(entry.Height);
                    stream.WriteByte(entry.ColorCount);
                    stream.WriteByte(entry.Reserved);

                    stream.Write(BitConverter.GetBytes((short)entry.Planes), 0, 2);
                    stream.Write(BitConverter.GetBytes((short)entry.BitCount), 0, 2);
                    stream.Write(BitConverter.GetBytes((int)entry.NumBytes), 0, 4);
                    stream.Write(BitConverter.GetBytes((int)(6 + 16 * entries.Count() +
                        data.Length)), 0, 4);

                    // Data
                    data.Write(bHeader, 0, bHeader.Length);
                }

                // Finish
                Byte[] temp = new Byte[data.Length];
                data.Seek(0, SeekOrigin.Begin);
                data.Read(temp, 0, temp.Length);
                data.Close();

                stream.Write(temp, 0, temp.Length);
                stream.Position = 0;

                temp = null;
            }
            catch
            {
                // Le fu
            }

        END:

            if (module != IntPtr.Zero)
                Native.FreeLibrary(module);

            return stream;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static bool EnumerateIcons(IntPtr hModule,
            IntPtr lpszType, IntPtr lpszName, Object lParam)
        {
            PtrReference Address = lParam as PtrReference;
            if (Address.p == IntPtr.Zero)
            {
                Address.p = lpszName;
                return false;
            }
            else
            {
                Address.p = IntPtr.Subtract(Address.p, 1);
                return true;
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static IconGroupHeader ParsePEIconHeader(Byte[] data)
        {
            IconGroupHeader header = new IconGroupHeader();
            header.Reserved = BitConverter.ToUInt16(data, 0);
            header.Type = BitConverter.ToUInt16(data, 2);
            header.Count = BitConverter.ToUInt16(data, 4);
            header.Entries = new IconDirEntry[header.Count];

            for (int i = 0; i < header.Count; i++)
            {
                header.Entries[i] = new IconDirEntry();

                header.Entries[i].Width = data[6 + 14 * i];
                header.Entries[i].Height = data[6 + 14 * i + 1];
                header.Entries[i].ColorCount = data[6 + 14 * i + 2];
                header.Entries[i].Reserved = data[6 + 14 * i + 3];

                header.Entries[i].Planes = BitConverter.ToUInt16(data, 6 + 14 * i + 4);
                header.Entries[i].BitCount = BitConverter.ToUInt16(data, 6 + 14 * i + 6);
                header.Entries[i].NumBytes = BitConverter.ToInt32(data, 6 + 14 * i + 8);
                header.Entries[i].Id = BitConverter.ToUInt16(data, 6 + 14 * i + 12);
            }

            return header;
        }
    }
}
